from pymodbus import Framer
from pymodbus.client import ModbusTcpClient as BaseModbusTcpClient
from datetime import datetime
from pymodbus.pdu import ModbusRequest
from service.common.message_queue import MessageQueue
from service.log import log


class ModbusRtuOverTcpClient(BaseModbusTcpClient):
    def __init__(self, host: str, port: int = 502):
        super().__init__(host=host, port=port, framer=Framer.RTU)
        self.message_queue = MessageQueue(max_size=1000)

    def execute(self, request: ModbusRequest):
        # 构造RTU帧（地址 + 功能码 + 数据 + CRC）
        rtu_frame = bytes([request.slave_id]) + bytes([request.function_code]) + request.encode()
        crc = self._calculate_crc(rtu_frame)
        full_rtu_request = rtu_frame + crc

        request_hex = " ".join(f"{b:02x}" for b in full_rtu_request)
        send_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        send_str = f"[{send_time}] RTU-over-TCP TX: {request_hex}"
        log.info(send_str)
        self.message_queue.enqueue(send_str)

        # 执行请求（通过TCP发送）
        response = super().execute(request)

        # 处理响应
        if response:
            response_rtu_frame = bytes([response.slave_id]) + bytes([response.function_code]) + response.encode()
            response_crc = self._calculate_crc(response_rtu_frame)
            full_rtu_response = response_rtu_frame + response_crc
            response_hex = " ".join(f"{b:02x}" for b in full_rtu_response)
            receive_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
            response_str = f"[{receive_time}] RTU-over-TCP RX: {response_hex}"
            log.info(response_str)
            self.message_queue.enqueue(response_str)
        return response

    def _calculate_crc(self, data: bytes) -> bytes:
        """同RTU客户端的CRC计算"""
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc = (crc >> 1) ^ 0xA001
                else:
                    crc >>= 1
        return bytes([crc & 0xFF, (crc >> 8) & 0xFF])

    def get_latest_message(self) -> list:
        """获取最新的消息"""
        message_list = []
        while not self.message_queue.is_empty():
            message_list.append(self.message_queue.dequeue())
        return message_list
